package storage

import (
	"context"
	"fmt"
	"github.com/minio/minio-go/v7"
	"io"
)

type MinIOStorage struct {
	cli *minio.Client
}

func NewMinIOStorage(cli *minio.Client) *MinIOStorage {
	return &MinIOStorage{
		cli: cli,
	}
}

func (m MinIOStorage) Upload(ctx context.Context, objectKey string, reader io.Reader, size int64, contentType string) error {
	bucketName, objectName := splitBucketAndObject(objectKey)
	if bucketName == "" {
		return fmt.Errorf("invalid bucket name %v", objectKey)
	}
	if objectName == "" {
		return fmt.Errorf("invalid object name %v", objectKey)
	}
	_, err := m.cli.PutObject(ctx, bucketName, objectName, reader, size, minio.PutObjectOptions{
		ContentType: contentType,
	})
	return err
}

func (m MinIOStorage) Download(ctx context.Context, objectKey string) (io.ReadCloser, error) {
	bucketName, objectName := splitBucketAndObject(objectKey)
	if bucketName == "" {
		return nil, fmt.Errorf("invalid bucket name %v", objectKey)
	}
	if objectName == "" {
		return nil, fmt.Errorf("invalid object name %v", objectKey)
	}
	return m.cli.GetObject(ctx, bucketName, objectKey, minio.GetObjectOptions{})
}

func (m MinIOStorage) Delete(ctx context.Context, objectKey string) error {
	bucketName, objectName := splitBucketAndObject(objectKey)
	if bucketName == "" {
		return fmt.Errorf("invalid bucket name %v", objectKey)
	}
	if objectName == "" {
		return fmt.Errorf("invalid object name %v", objectKey)
	}
	return m.cli.RemoveObject(ctx, bucketName, objectKey, minio.RemoveObjectOptions{})
}

func (m MinIOStorage) Size(ctx context.Context, objectKey string) (int64, error) {
	bucketName, objectName := splitBucketAndObject(objectKey)
	if bucketName == "" {
		return -1, fmt.Errorf("invalid bucket name %v", objectKey)
	}
	if objectName == "" {
		return -1, fmt.Errorf("invalid object name %v", objectKey)
	}
	objInfo, err := m.cli.StatObject(ctx, bucketName, objectKey, minio.StatObjectOptions{})
	if err != nil {
		return 0, err
	}
	return objInfo.Size, nil
}

func splitBucketAndObject(objectKey string) (bucketName, objectName string) {
	bucketName, objectName = objectKey, ""
	if idx := len(bucketName) - 1; idx > 0 {
		if bucketName[idx] == '/' {
			bucketName = bucketName[:idx]
		}
		if idx := len(bucketName) - 1; idx > 0 {
			if bucketName[idx] != '/' {
				objectName = bucketName[idx+1:]
				bucketName = bucketName[:idx]
			}
		}
	}
	return bucketName, objectName
}
